package com.esdk.utils;

import com.esdk.esdk;
import lombok.NonNull;

import java.io.InputStream;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;


/**
 * @author ffychina@163.com
 * @since  2010
 * 可以加载多个properties文件，最后读入的文件会覆盖前面的属性，即优先级最高。
 * 提供导入机制，可以包含其他的properties文件，已避免properties文件死循环导入。
 * 统一把句号分隔和驼峰格式转换成下划线分隔的小写key
 * */
public class EasyProp{
	private final Properties _prop=esdk.map.merge(new Properties(),System.getProperties());
	public LinkedHashSet<String> propFileSet=new LinkedHashSet();
	private boolean inited=false;
	public EasyProp(Properties... props){
		for(Properties prop:props) {
			this._prop.putAll(adjustPropertiesKey(prop));
		}
	}
	public EasyProp(String... propertiesFileNames){
//		esdk.sout("正在创建EasyProp实例：{}...",esdk.str.valueOf(propertiesFileNames));
		for(String propFilename:propertiesFileNames) {
			InputStream is=getProfileInputStream(propFilename);
			if(is!=null)
				_prop.putAll(adjustPropertiesKey(esdk.file.getProperties(is)));
		}
//		esdk.sout("已成功创建EasyProp实例：",esdk.str.valueOf(_prop));
	}

	public @NonNull Properties getProperties(){
		if(!inited){
			for(Object key: _prop.keySet()){
				get((String)key);
			}
			inited=true;
		}
		return _prop;
	}


	public @NonNull Properties getProfileProperties(@NonNull String propertiesFileName){
		return esdk.file.getProperties(getProfileInputStream(propertiesFileName));
	}

	public InputStream getProfileInputStream(@NonNull String propertiesFileName){
		propertiesFileName=esdk.str.addEnd(propertiesFileName,".properties");
		if(propFileSet.contains(propertiesFileName)){
			return null;
		}
//		esdk.sout("正在读取资源文件：{}...",propertiesFileName);
		String[] filename=propertiesFileName.split("\\.");
		String profile=getActive();
		InputStream resourceAsStream=null;
		if(esdk.str.isValid(profile)&&!filename[0].endsWith(profile)){
			String profileFileName=filename[0]+profile+"."+filename[1];
			resourceAsStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(profileFileName);
			if(resourceAsStream!=null)
				System.out.println("已成功读取profile资源文件："+profileFileName);
		}
		if(resourceAsStream==null){
			resourceAsStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(propertiesFileName);
			if(resourceAsStream!=null)
				System.out.println("已读取资源文件："+propertiesFileName);
		}
		propFileSet.add(propertiesFileName);
		return resourceAsStream;
	}

	/**统一把句号分隔和驼峰格式转换成下划线分隔的小写key*/
	public Properties adjustPropertiesKey(Properties prop){
		if(prop==null) return null;
		Properties newProp=new Properties();
		importPropertiesFiles(prop,newProp);
		for(Map.Entry objectObjectEntry: prop.entrySet()){
			String key=adjustToUnifyKey(objectObjectEntry.getKey().toString());
			newProp.put(key,objectObjectEntry.getValue());
		}
		prop.clear();
		prop.putAll(newProp);
		return prop;
	}

	private void importPropertiesFiles(@NonNull Properties prop,@NonNull Properties newProp){
		String importPropFiles=(String)prop.get(adjustToUnifyKey("import_prop_files"));
		if(importPropFiles!=null){
			String propFiles[]=esdk.str.split(importPropFiles);
			System.out.println("正在引入其他资源文件："+importPropFiles);
			for(String propFileName:propFiles){
				Properties profileProperties=getProfileProperties(propFileName);
				if(profileProperties!=null)
					newProp.putAll(adjustPropertiesKey(profileProperties));
			}
		}
	}

	public static String adjustToUnifyKey(@NonNull String key){
		String result=adjustToUnifyKey(key,".");
		return result;
	}

	private static String adjustToUnifyKey(@NonNull String key,@NonNull String replacement){
		String result=esdk.str.toUnderlineCase(key).replace("_",replacement);
		return result;
	}

	public boolean containsKey(@NonNull String key){
		return _prop.containsKey(adjustToUnifyKey(key));
	}

	public EasyProp put(@NonNull String key,Object value){
		_prop.put(adjustToUnifyKey(key),value);
		return this;
	}

	public EasyProp putIfAbsent(@NonNull String key,Object value){
		_prop.putIfAbsent(adjustToUnifyKey(key),value);
		return this;
	}


	public String get(@NonNull String key) {
		return getString(key);
	}
	
	public String get(@NonNull String key,@NonNull String defVal) {
		return getString(key,defVal);
	}
	
	public String getString(@NonNull String key) {
		key=adjustToUnifyKey(key);
		String value=_prop.getProperty(key);
		if(value!=null&&value.contains("${")) {
			value=esdk.regex.replaceAll(value,"\\$\\{(.+?)\\}",(m)->"${"+adjustToUnifyKey(m.group(1))+"}");
			value=esdk.str.deepFormat(value,true,_prop);
			_prop.setProperty(key,value);
		}
		return value;
	}

	public String[] getArray(@NonNull String key) {
		return esdk.str.splits(get(key));
	}

	public String[] getArray(@NonNull String key,@NonNull String defValues) {
		return esdk.str.splits(get(key,defValues));
	}

	public String[] getArray(@NonNull String key,@NonNull String[] defValues) {
		return esdk.obj.or(esdk.str.splits(get(key)),()->defValues);
	}
	
	public String getString(@NonNull String key,@NonNull String defValue) {
		return esdk.obj.or(getString(key),defValue);
	}

	public Integer getInteger(@NonNull String key) {
		return esdk.math.toInteger(getString(key));
	}

	public Integer getInteger(@NonNull String key,@NonNull Integer defValue) {
		return esdk.obj.or(getInteger(key),defValue);
	}
	public Long getLong(@NonNull String key) {
		return esdk.math.toLong(getString(key));
	}

	public Long getLong(@NonNull String key,@NonNull Long defValue) {
		return esdk.obj.or(getLong(key),defValue);
	}
	public Double getDouble(@NonNull String key) {
		return esdk.math.toDouble(getString(key));
	}

	public Double getDouble(@NonNull String key,@NonNull Double defValue) {
		return esdk.obj.or(getDouble(key),defValue);
	}

	public Boolean getBoolean(@NonNull String key) {
		Object value=getString(key);
		if(value!=null)
			return esdk.obj.isTrue(value);
		else
			return null;
	}
	
	public Boolean getBoolean(@NonNull String key,@NonNull Boolean defValue) {
		return esdk.obj.ifNull(getBoolean(key),defValue);
	}

	public static @NonNull String getActive(){
		String active=System.getProperty("nutz.profiles.active");
		if(active!=null){
			active="-"+active;
		}else
			active="";
		return active;
	}

	public static void main(String[] args){
		EasyProp prop=new EasyProp("esdk","project.properties");
		esdk.tool.assertEquals(prop.get("LogName"),"esdk");
		esdk.tool.assertEquals(prop.get("LogFilePath","/logs/sql.log"),"/datadisk/logs/esdk_sql.log");
		esdk.tool.assertEquals(prop.get("log_slow_queries"),"/datadisk/logs/esdk_slow_sql.log");
		esdk.tool.assertEquals(prop.get("project.name"),"esdk");
		esdk.tool.assertEquals(prop.get("project_name"),"esdk");
		esdk.tool.assertEquals(prop.get("ProjectName"),"esdk");
		esdk.tool.assertEquals(prop.getBoolean("IsShowConnectionStatus"),false);
		esdk.tool.assertEquals(prop.getString("LogName"),prop.get("ProjectName"));
		esdk.sout(prop.getProperties());
	}

}
